<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>服务端开发</title>
    <meta name="generator" content="VuePress 1.5.2">
    <link rel="apple-touch-icon" href="/blog/apple-touch-icon.png">
    <link rel="icon" href="/blog/favicon.ico">
    <link rel="manifest" href="/blog/manifest.json">
    <link rel="mask-icon" href="/blog/icons/icon.svg" color="#ffffff">
    <meta name="description" content="可能是全网最给力的前端博客">
    <meta name="theme-color" content="#ffffff">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="default">
    <meta name="msapplication-TileImage" content="/icons/icon-144x144.png">
    <link rel="preload" href="/blog/assets/css/0.styles.3f022aa2.css" as="style"><link rel="preload" href="/blog/assets/js/app.7fa07907.js" as="script"><link rel="preload" href="/blog/assets/js/2.b6c629b5.js" as="script"><link rel="preload" href="/blog/assets/js/18.a82e534b.js" as="script"><link rel="preload" href="/blog/assets/js/3.589a64d8.js" as="script"><link rel="prefetch" href="/blog/assets/js/10.8e59817e.js"><link rel="prefetch" href="/blog/assets/js/11.34cd2f44.js"><link rel="prefetch" href="/blog/assets/js/12.445d88fd.js"><link rel="prefetch" href="/blog/assets/js/13.e6460b96.js"><link rel="prefetch" href="/blog/assets/js/14.d0ee0fcf.js"><link rel="prefetch" href="/blog/assets/js/15.e91e78ed.js"><link rel="prefetch" href="/blog/assets/js/16.8310de97.js"><link rel="prefetch" href="/blog/assets/js/17.6ca97902.js"><link rel="prefetch" href="/blog/assets/js/19.e3a914ee.js"><link rel="prefetch" href="/blog/assets/js/20.6db77b80.js"><link rel="prefetch" href="/blog/assets/js/21.e705be23.js"><link rel="prefetch" href="/blog/assets/js/22.efc39414.js"><link rel="prefetch" href="/blog/assets/js/23.5f4c7658.js"><link rel="prefetch" href="/blog/assets/js/24.359a62e4.js"><link rel="prefetch" href="/blog/assets/js/25.c49ef7a9.js"><link rel="prefetch" href="/blog/assets/js/26.8d45691f.js"><link rel="prefetch" href="/blog/assets/js/27.364b1802.js"><link rel="prefetch" href="/blog/assets/js/28.485228c0.js"><link rel="prefetch" href="/blog/assets/js/29.0ee8fc42.js"><link rel="prefetch" href="/blog/assets/js/30.465920a7.js"><link rel="prefetch" href="/blog/assets/js/4.1dbf8b08.js"><link rel="prefetch" href="/blog/assets/js/5.9253897f.js"><link rel="prefetch" href="/blog/assets/js/6.4c1f13e3.js"><link rel="prefetch" href="/blog/assets/js/7.bc1839d0.js"><link rel="prefetch" href="/blog/assets/js/8.611f5ec5.js"><link rel="prefetch" href="/blog/assets/js/9.5813bb74.js">
    <link rel="stylesheet" href="/blog/assets/css/0.styles.3f022aa2.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/blog/" class="home-link router-link-active"><img src="/blog/app.png" alt="卢本伟广场" class="logo"> <span class="site-name can-hide">卢本伟广场</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/blog/dsm/" class="nav-link router-link-active">
  歪比歪比
</a></div><div class="nav-item"><a href="/blog/lbw/" class="nav-link">
  歪比吧卜
</a></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/blog/dsm/" class="nav-link router-link-active">
  歪比歪比
</a></div><div class="nav-item"><a href="/blog/lbw/" class="nav-link">
  歪比吧卜
</a></div> <!----></nav>  <ul class="sidebar-links"><li><a href="/blog/dsm/" aria-current="page" class="sidebar-link">前端自我修养</a></li><li><a href="/blog/dsm/algorithm.html" class="sidebar-link">前端面试算法</a></li><li><a href="/blog/dsm/jsbook.html" class="sidebar-link">JS高级程序设计</a></li><li><a href="/blog/dsm/cc150.html" class="sidebar-link">程序员面试金典</a></li><li><a href="/blog/dsm/pattern.html" class="sidebar-link">前端设计模式</a></li><li><a href="/blog/dsm/architecture.html" class="sidebar-link">前端工程架构</a></li><li><a href="/blog/dsm/protocol.html" class="sidebar-link">计算机网络</a></li><li><a href="/blog/dsm/node.html" aria-current="page" class="active sidebar-link">服务端开发</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#nodejs概述" class="sidebar-link">Nodejs概述</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#nodejs应用" class="sidebar-link">Nodejs应用</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#nodejs特点" class="sidebar-link">Nodejs特点</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#异步编程" class="sidebar-link">异步编程</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#commonjs模块化规范" class="sidebar-link">CommonJS模块化规范</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#模块编译" class="sidebar-link">模块编译</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#fs模块" class="sidebar-link">fs模块</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#post接口-打印日志" class="sidebar-link">POST接口+打印日志</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#网络安全" class="sidebar-link">网络安全</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#浏览器对非同源网站的限制" class="sidebar-link">浏览器对非同源网站的限制</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#跨域的9种方式" class="sidebar-link">跨域的9种方式</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#rest架构" class="sidebar-link">REST架构</a></li><li class="sidebar-sub-header"><a href="/blog/dsm/node.html#api设计最佳实践" class="sidebar-link">API设计最佳实践</a></li></ul></li><li><a href="/blog/dsm/webgl.html" class="sidebar-link">图形3D开发</a></li><li><a href="/blog/dsm/lodash.html" class="sidebar-link">jQuery / Lodash</a></li><li><a href="/blog/dsm/finger.html" class="sidebar-link">AlloyFinger</a></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="服务端开发"><a href="#服务端开发" class="header-anchor">#</a> 服务端开发</h1> <img id="topImg" src="/blog/images/IMG_9022.JPG"> <h2 id="nodejs概述"><a href="#nodejs概述" class="header-anchor">#</a> Nodejs概述</h2> <p>Nodejs作为服务端宿主环境不提供DOM和BOM，但与浏览器有着相同的运行机制和原理因为是基于Chrome浏览器V8引擎，打破了JS只能在浏览器中运行的局面，前后端编程环境统一，大大降低前后度端转换所需要的成本，令前端开发者能够以相对较小的成本跨界服务端。</p> <h2 id="nodejs应用"><a href="#nodejs应用" class="header-anchor">#</a> Nodejs应用</h2> <ul><li>命令行工具</li> <li>全栈式开发</li> <li>中间层跨域</li> <li>伪接口测试</li> <li>服务端渲染</li></ul> <h2 id="nodejs特点"><a href="#nodejs特点" class="header-anchor">#</a> Nodejs特点</h2> <ul><li><strong>单线程</strong>：多线程之间的切换开销较大，经常面临死锁、状态同步等问题</li> <li><strong>异步编程</strong>：不会因计时器、网络、文件、数据库等系统操作阻塞同步代码，导致CPU得不到更好利用</li></ul> <h2 id="异步编程"><a href="#异步编程" class="header-anchor">#</a> 异步编程</h2> <p><strong>基于函数式编程，Node所有API都支持回调函数，异步编程依托于回调来实现</strong></p> <p>异步编程的难点：</p> <ul><li><strong>异常处理</strong>：如果异步回调函数报错，try/catch是无法捕捉到的，Node在处理异常上形成了一种约定，将异常作为回调函数的第一个实参</li> <li><strong>服务雪崩</strong>：高并发场景下，相同的请求会影响服务器性能</li> <li><strong>异步协同</strong>：如果并行的异步函数有依赖关系，但返回结果不按理想顺序</li> <li><strong>回调地狱</strong>：如果是串行的异步函数，随着逻辑变复杂，每增加一层异步操作，就会至少多一层回调函数嵌套，如果还考虑到异常处理，代码到了后期非常不易阅读和维护</li> <li><strong>阻塞线程</strong>：早期JS没有sleep()沉睡线程的功能，定时器不能阻塞单线程后续同步代码，在不占用CPU的情况下很难实现</li></ul> <p>解决方案：</p> <ul><li><strong>工具函数</strong></li> <li><strong>发布订阅模式</strong></li> <li><strong>async/await</strong></li></ul> <h2 id="commonjs模块化规范"><a href="#commonjs模块化规范" class="header-anchor">#</a> CommonJS模块化规范</h2> <ul><li><strong>module.exports = {}</strong></li> <li><strong>exports = module.exports</strong></li> <li><strong>require()是同步加载的，将引入模块的同步异步任务执行完才执行下一行，返回的是module.exports</strong></li> <li><strong>require()在同一个文件多次引入同一个模块，会使用系统缓存直接返回加载好的第一次引入的结果</strong></li> <li><strong>require('xxx')是加载内置或第三方模块，require('./xxx')是加载自定义模块</strong></li> <li><strong>CommonJS模块导出的是浅拷贝，ES6模块导出的是只读引用</strong></li> <li><strong>为了在Webpack中使用tree shaking必须使用ES6模块</strong></li></ul> <h2 id="模块编译"><a href="#模块编译" class="header-anchor">#</a> 模块编译</h2> <p>本质上每个js文件都是一个新的Module类实例module，最终会被编译进一个函数内，实参有exports、require、module、__filename、__dirname，所以每个js文件才能访问这些变量，并且每个文件的module.exports都是独立的</p> <div class="language-js extra-class"><pre class="language-js"><code>
<span class="token operator">!</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
    <span class="token comment">//自己写的Nodejs代码会被包装在这</span>
    <span class="token keyword">return</span> module<span class="token punctuation">.</span>exports
<span class="token punctuation">}</span><span class="token punctuation">(</span>exports<span class="token punctuation">,</span>require<span class="token punctuation">,</span>module<span class="token punctuation">,</span>__filename<span class="token punctuation">,</span>__dirname<span class="token punctuation">)</span>

</code></pre></div><h2 id="fs模块"><a href="#fs模块" class="header-anchor">#</a> fs模块</h2> <ul><li><strong>readdirSync('./')</strong>：同步地读取当前文件目录，返回一个包含目录下所有文件名的数组</li> <li><strong>readdir('./',(err,data)=&gt;console.log(data))</strong>：异步地读取当前文件目录，并打印出包含该目录下所有文件名的数组，没返回值</li> <li><strong>readFileSync('./index.js','utf8')</strong>：同步地读取当前目录的index.js文件，返回utf8编码格式内容的字符串；如果是图片或者音视频资源，用utf8编码格式会乱码，默认使用buffer文件流格式</li> <li><strong>readFile('./index.js','utf8',(err,data)=&gt;console.log(data))</strong>：异步地读取当前目录的index.js文件，并打印出该文件中的内容，没返回值</li> <li><strong>writeFileSync('./index.js','hello','utf8')</strong>：向当前目录的index.js文件覆盖式地写入内容'hello'，如果文件不存在，创建该文件再写'hello'</li> <li><strong>writeFile('./index.js','hello','utf8',err=&gt;console.log(err))</strong>：异步地向index.js写入内容</li> <li><strong>appendFileSync('./index.js','hello','utf8')</strong>：同步地给index.js文件尾部加入内容</li> <li><strong>appendFile('./index.js','hello','utf8',err=&gt;console.log(err))</strong>：异步地给index.js文件尾部加入内容</li> <li><strong>copyFile('./index.js','./copy.js',err=&gt;console.log(err))</strong>：拷贝出来的新文件如果和已有文件重名，会覆盖掉已有文件</li> <li><strong>unlink('./img/1.png',err=&gt;console.log(err))</strong>：删除文件1.png</li> <li><strong>mkdir('./img',err=&gt;console.log(err))</strong>：创建目录img</li> <li><strong>rmdir('./img',err=&gt;console.log(err))</strong>：删除目录img，且一定要保证目录里没有文件和子目录</li></ul> <h2 id="post接口-打印日志"><a href="#post接口-打印日志" class="header-anchor">#</a> POST接口+打印日志</h2> <div class="language-js extra-class"><pre class="language-js"><code>
<span class="token keyword">const</span> Koa <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'koa'</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> Router <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'koa-router'</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> bodyParser <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'koa-bodyparser'</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'fs'</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Koa</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> api <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Router</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> <span class="token constant">PORT</span> <span class="token operator">=</span> <span class="token number">3000</span>

<span class="token comment">// axios.post(&quot;localhost:3000&quot;, {event:'pageview',url:'localhost:8080',th_app_id:'web'}).then(res =&gt; console.log(res)).catch(err =&gt; console.log(err))</span>
<span class="token comment">// localhost:3000?event=pageview&amp;url=localhost:8080&amp;th_app_id=web</span>

<span class="token comment">// axios.post(&quot;localhost:3000&quot;, {event:'pageloadDuration',url:'localhost:8080',th_app_id:'web',duration:500}).then(res =&gt; console.log(res)).catch(err =&gt; console.log(err))</span>
<span class="token comment">// localhost:3000?event=pageloadDuration&amp;url=localhost:8080&amp;th_app_id=web&amp;duration=500</span>

<span class="token comment">// axios.post(&quot;localhost:3000&quot;, {event:'networkRequestMonitor',url:'localhost:8080',th_app_id:'web',duration:500,httpCode:200}).then(res =&gt; console.log(res)).catch(err =&gt; console.log(err))</span>
<span class="token comment">// localhost:3000?event=networkRequestMonitor&amp;url=localhost:8080&amp;th_app_id=web&amp;duration=500&amp;httpCode=200</span>

<span class="token comment">// axios.post(&quot;localhost:3000&quot;, {event:'clickElement',elementId:'cart',th_app_id:'web',productId:'AP-GP-CL'}).then(res =&gt; console.log(res)).catch(err =&gt; console.log(err))</span>
<span class="token comment">// localhost:3000?event=clickElement&amp;elementId=cart&amp;th_app_id=web&amp;productId=AP-GP-CL</span>

app
  <span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span><span class="token function">bodyParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>api<span class="token punctuation">.</span><span class="token function">routes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>api<span class="token punctuation">.</span><span class="token function">allowedMethods</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

api<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token parameter">ctx</span> <span class="token operator">=&gt;</span> ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> ctx<span class="token punctuation">.</span>request<span class="token punctuation">.</span>query<span class="token punctuation">)</span>

api<span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token parameter">ctx</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>

    <span class="token keyword">let</span> body <span class="token operator">=</span> ctx<span class="token punctuation">.</span>body <span class="token operator">=</span> ctx<span class="token punctuation">.</span>request<span class="token punctuation">.</span>body
  
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>body<span class="token punctuation">)</span>
    
    <span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>body<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">'\n\n'</span>

    fs<span class="token punctuation">.</span><span class="token function">appendFileSync</span><span class="token punctuation">(</span><span class="token string">'./record.txt'</span><span class="token punctuation">,</span>content<span class="token punctuation">)</span>

    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>body<span class="token punctuation">.</span>event<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> 事件 检测成功</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
  
<span class="token punctuation">}</span><span class="token punctuation">)</span>

app<span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span><span class="token constant">PORT</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'server is running at http://localhost:'</span> <span class="token operator">+</span> <span class="token constant">PORT</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

</code></pre></div><h2 id="网络安全"><a href="#网络安全" class="header-anchor">#</a> 网络安全</h2> <ul><li><strong>XSS（Cross Site Scripting）跨站脚本攻击</strong> <ul><li>原理：页面渲染的数据中包含可运行的脚本，攻击类型有反射型（url参数直接注入）和存储型（存储到DB后读取时注入），可以注入HTML元素和JS代码</li> <li>危害：利用虚假输入表单骗取用户个人信息；利用脚本窃取用户的Cookie值，被害者在不知情的情况下，帮助攻击者发送恶意请求；显示伪造的文章或图片</li> <li>防御：
<ul><li>建立CSP（Content Security Policy）提供资源的网址白名单</li> <li>设置表单输入的格式，前端服务端过滤危险标签，转义危险字符</li></ul></li></ul></li> <li><strong>CSRF（Cross Site Request Forgery）跨站请求伪造</strong> <ul><li>原理：在第三方网站向本网站发起请求，只要用户访问了第三方网站的前端页面，第三方网站就可以在用户完全不知道的情况下，带着本网站的用户登录态（cookie）向本网站发起请求，可以和XSS结合使用变成XSS蠕虫攻击</li> <li>危害：盗用用户身份来转账、发帖、完成业务请求</li> <li>防御：前端服务端进行Anti-CSRF-Token验证</li></ul></li> <li><strong>DDOS（Distributed Denial of Service）分布式拒绝服务</strong> <ul><li>原理：通过大量非正常的请求，耗尽服务器的资源，使得服务器不能正常地运行，不能为合法用户服务</li> <li>危害：攻击流量超过服务网络设备的极限或带宽的最大负荷，网站服务将瘫痪导致无法正常运作</li> <li>防御：
<ul><li>验证码</li> <li>限制请求频率</li> <li>扩容加带宽</li> <li>分布式服务</li> <li>云防御服务</li></ul></li></ul></li> <li><strong>HTTP劫持</strong> <ul><li>原理：运营商利用http明文传输的特性，篡改了服务端到浏览器的网页内容</li> <li>危害：给正常网站加入额外的广告</li> <li>防御：使用HTTPS协议</li></ul></li> <li><strong>点击劫持</strong> <ul><li>原理：点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 iframe 嵌套的方式嵌入自己的网页中，并将 iframe 设置为透明，在页面中透出一个按钮诱导用户点击</li> <li>危害：骗取用户点击操作</li> <li>防御：给HTTP响应头设置X-FRAME-OPTIONS：DENY来禁止本页面通过iframe展示</li></ul></li> <li><strong>中间人攻击</strong> <ul><li>原理：中间人截取客户端发送给服务器的请求，然后伪装成客户端与服务器进行通信；将服务器返回给客户端的内容发送给客户端，伪装成服务器与客户端进行通信</li> <li>危害：客户端如果信任中间人的证书，中间人便可以获取客户端和服务器之间通信的所有内容</li> <li>防御：
<ul><li>客户端不要轻易信任弹出的可疑证书</li> <li>不在公共Wi-Fi网络上购买或发送敏感数据，因为攻击者能用SSL剥离技术，把HTTPS降级为HTTP</li></ul></li></ul></li></ul> <h2 id="浏览器对非同源网站的限制"><a href="#浏览器对非同源网站的限制" class="header-anchor">#</a> 浏览器对非同源网站的限制</h2> <p>同源策略(Same origin Policy): 浏览器出于安全方面的考虑，只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下，不能读写对方的资源。</p> <ul><li>非同源网站的Cookie、LocalStorage 和 IndexDB 无法读取</li> <li>非同源网站的DOM无法获取</li> <li>向非同源服务器发送的AJAX请求，其响应会被浏览器拒绝</li></ul> <p>由于前后端分离的开发模式，哪怕只是前端和后端运行端口不一致，也算非同源，也算跨域，AJAX响应也会被拒绝，但必须要跨域，因为前端必须要拿到后端的数据。</p> <h2 id="跨域的9种方式"><a href="#跨域的9种方式" class="header-anchor">#</a> 跨域的9种方式</h2> <ul><li>JSONP（只能发GET请求）</li> <li>CORS（主流）</li> <li>Node中间件代理（主流）</li> <li>Ngix反向代理（没前端什么事）</li> <li>PostMessage（非主流）</li> <li>WebSocket（非主流）</li> <li>window.name+iframe（非主流）</li> <li>location.hash+iframe（非主流）</li> <li>document.domain+iframe（非主流）</li></ul> <h2 id="rest架构"><a href="#rest架构" class="header-anchor">#</a> REST架构</h2> <p>REST（Representational State Transfer）表象层状态转变指的是一组架构约束条件和原则，它由Roy Fielding于2000年论文中提出，它代表着分布式服务的架构风格。如果一个架构符合REST的约束条件和原则，我们就称它为REST架构。</p> <p>REST六大原则：</p> <ul><li>客户端-服务端（Client-Server）
<ul><li>前后端关注点分离</li> <li>服务端专注数据存储，提升了简单性</li> <li>前端关注数据渲染，提升了可移植性</li></ul></li> <li>无状态（Stateless）
<ul><li>所有用户会话信息都保存在客户端</li> <li>客户端每次请求必须包括所有信息</li> <li>服务端不用保存会话信息，提升了简单性、可靠性、可见性</li></ul></li> <li>缓存（Cache）
<ul><li>所有服务端响应都要被标为可缓存或不可缓存</li> <li>减少前后端交互，提升性能</li></ul></li> <li>统一接口（Uniform Interface）
<ul><li>接口设计尽可能统一通用，提升了简单性、可见性</li> <li>接口与实现解耦，使前后端可以独立开发迭代</li></ul></li> <li>分层系统（Layered System）
<ul><li>每层只知道相邻的一层，后面隐藏的就不知道</li> <li>客户端不知道是和代理还是真实服务器通信</li> <li>其他层：安全层、负载均衡、缓存层等</li></ul></li> <li>按需代码（Code-On-Demand）
<ul><li>客户端可以下载运行服务器端传来的代码</li> <li>通过减少一些功能，简化了客户端</li></ul></li></ul> <p>在前后端分离的背景下，RESTful API就是符合REST架构风格的API，用来设计便于理解，容易使用的API的设计规范。一个RESTful API一般包含三方面：</p> <ul><li>基本的URL，如<strong>https://api.github.com/users</strong></li> <li>标准的HTTP方法，如GET、POST、PUT、PATCH、DELETE</li> <li>传输的数据媒体类型，如JSON、XML</li></ul> <p>GitHub REST API示例：</p> <ul><li><strong>POST /user/repos</strong>（Create a new repository）</li> <li><strong>DELETE /repos/:owner/:repo</strong>（Delete a repository）</li> <li><strong>PATCH /repos/:owner/:repo</strong>（Edit a repository）</li> <li><strong>PUT /repos/:owner/:repo/topics</strong>（Replace all topics for a repository）</li> <li><strong>POST /repos/:owner/:repo/transfer</strong>（Transfer a repository）</li> <li><strong>GET /user/repos</strong>（List your repositories）</li> <li><strong>GET /users/:username/repos</strong>（List user repositories）</li> <li><strong>GET /orgs/:org/repos</strong>（List organization repositories）</li></ul> <h2 id="api设计最佳实践"><a href="#api设计最佳实践" class="header-anchor">#</a> API设计最佳实践</h2> <p><strong>路由</strong></p> <ul><li>不同的URL展示不同的页面</li> <li>单页面应用URL切换时页面无刷新</li></ul> <p><strong>请求规范</strong></p> <ul><li>URL使用名词，尽量使用复数，如/users</li> <li>URL使用嵌套表示关联关系，如/users/12/repo/5</li> <li>使用正确的HTTP方法，如GET/POST/PUT/DELETE</li> <li>不符合CRUD的情况：POST/action/子资源</li> <li>不同的HTTP请求应该得到不同的数据</li></ul> <p><strong>响应规范</strong></p> <ul><li>过滤查询</li> <li>分页查询</li> <li>字段过滤</li> <li>状态码</li> <li>错误处理</li></ul> <p><strong>安全</strong></p> <ul><li>HTTPS</li> <li>鉴权</li> <li>限流</li></ul> <p><strong>开发者友好</strong></p> <ul><li>文档</li> <li>超媒体</li></ul></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">上次更新:</span> <span class="time">2/17/2021, 11:27:55 PM</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/blog/dsm/protocol.html" class="prev">
        计算机网络
      </a></span> <span class="next"><a href="/blog/dsm/webgl.html">
        图形3D开发
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"><BackToTop></BackToTop><!----><Live2D></Live2D></div></div>
    <script src="/blog/assets/js/app.7fa07907.js" defer></script><script src="/blog/assets/js/2.b6c629b5.js" defer></script><script src="/blog/assets/js/18.a82e534b.js" defer></script><script src="/blog/assets/js/3.589a64d8.js" defer></script>
  </body>
</html>
